WorkManager基本使用及源码分析(六) - SystemForegroundService

2021年02月06日 99 字 Jetpack


目录


源码篇

上一篇中我们了解了WorkManager使用的的主要组件,猜测了各个组件的作用,并简单介绍了WorkManager是如何初始化的。本篇将延续前文,介绍WorkManager中Service组件之一的SystemForegroundService。

SystemForegroundService

在看过前面SystemAlarmService & SystenJobService 后,我本以为
SystemForegroundService也如前者一般是处理任务执行的,但简单阅读代码后发现并非如此,尽管结构上与前者类似,但其主要作用是给运行任务的服务提权,保证其在前台执行

SystemAlarmService & SystenJobService相似,SystemForegroundService也是LifecycleService的子类,主要方法有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
public void onCreate() {
super.onCreate();
sForegroundService = this;
initializeDispatcher();
}

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
// 若正在被关闭,则
if (mIsShutdown) {
// 结束旧 Dispatcher 的生命周期
mDispatcher.onDestroy();
// 创建新的Dispatcher并设置新的生命周期
initializeDispatcher();
mIsShutdown = false;
}
if (intent != null) {
// 告诉Dispatcher 有活儿来了
mDispatcher.onStartCommand(intent);
}
// 如果服务崩溃,我们希望所有未确认的意图都得到重新交付。
return Service.START_REDELIVER_INTENT;
}
@MainThread
private void initializeDispatcher() {
mHandler = new Handler(Looper.getMainLooper());
mNotificationManager = (NotificationManager)
getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
mDispatcher = new SystemForegroundDispatcher(getApplicationContext());
mDispatcher.setCallback(this);
}

初始化时创建新的SystemForegroundDispatcher绑定并设置新的生命周期,当收到指令被启动时,调用SystemForegroundDispatcher.onStartCommand():

1
2
3
4
5
6
7
8
9
10
11
12
13
// SystemForegroundDispatcher#onStartCommand
void onStartCommand(@NonNull Intent intent) {
String action = intent.getAction();
if (ACTION_START_FOREGROUND.equals(action)) {
handleStartForeground(intent);
// 调用handleNotify(),它反过来调用start前台()作为处理这个命令的一部分。这对一些原始设备制造商来说很重要。
handleNotify(intent);
} else if (ACTION_NOTIFY.equals(action)) {
handleNotify(intent);
} else if (ACTION_CANCEL_WORK.equals(action)) {
handleCancelWork(intent);
}
}

当intent (此处intent可视作前面提到的SystemXXXService的意图) 状态处于启动、更新状态时会调用handleNotify(intent),取消时会调用handleCancelWork(intent),
状态为取消时,则最终会调用mWorkManagerImpl.cancelWorkById(UUID.fromString(workSpecId))取消任务执行。

我们再来看看handleNotify干了哪些事情吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// SystemForegroundDispatcher#handleNotify
@MainThread
private void handleNotify(@NonNull Intent intent) {
int notificationId = intent.getIntExtra(KEY_NOTIFICATION_ID, 0);
int notificationType = intent.getIntExtra(KEY_FOREGROUND_SERVICE_TYPE, 0);
String workSpecId = intent.getStringExtra(KEY_WORKSPEC_ID);
Notification notification = intent.getParcelableExtra(KEY_NOTIFICATION);
if (notification != null && mCallback != null) {
ForegroundInfo info = new ForegroundInfo(
notificationId, notification, notificationType);
// 缓存信息
mForegroundInfoById.put(workSpecId, info);
if (TextUtils.isEmpty(mCurrentForegroundWorkSpecId)) {
// 这是拥有前台生命周期的当前workSpecId.
mCurrentForegroundWorkSpecId = workSpecId;
mCallback.startForeground(notificationId, notificationType, notification);
} else {
// 更新通知
mCallback.notify(notificationId, notification);
// 如有必要,更新前台的通知,使其成为当前所有前台服务类型的联合。
if (notificationType != FOREGROUND_SERVICE_TYPE_NONE
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
int foregroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
for (Map.Entry<String, ForegroundInfo> entry : mForegroundInfoById.entrySet()) {
ForegroundInfo foregroundInfo = entry.getValue();
foregroundServiceType |= foregroundInfo.getForegroundServiceType();
}
//缓存中取出信息
ForegroundInfo currentInfo = mForegroundInfoById.get(mCurrentForegroundWorkSpecId);
if (currentInfo != null) {
mCallback.startForeground(
currentInfo.getNotificationId(),
foregroundServiceType,
currentInfo.getNotification()
);
}
}
}
}
}

若当前服务intent是首个,那么就启动SystemForegroundService.startForeground()做周期性递归,而SystemForegroundService.startForeground()实际上是一个空方法,类似于使用了Looper保证SystemForegroundService一直运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// SystemForegroundService#startForeground
@Override
public void startForeground(
final int notificationId,
final int notificationType,
@NonNull final Notification notification) {

mHandler.post(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(notificationId, notification, notificationType);
} else {
startForeground(notificationId, notification);
}
}
});
}

若当前服务intent不是首个,则更新一则通知,并合并前台服务类型,mCallback.notify(notificationId, notification)

1
2
3
4
5
6
7
8
9
10
// SystemForegroundService#notify
@Override
public void notify(final int notificationId, @NonNull final Notification notification) {
mHandler.post(new Runnable() {
@Override
public void run() {
mNotificationManager.notify(notificationId, notification);
}
});
}

总结

SystemForegroundService会在任务启动时启动,且会创建一个类似于Looper的结构保持服务前台持续运行,后续有任务触发时,会发送notification以保证其执行任务的服务可被视作前台服务保持运行。